import { dirname, join, sep } from "path";
import { globSync } from 'glob'
import { ImportKind, context, BuildOptions, Plugin } from 'esbuild'
import { sassPlugin, postcssModules } from 'esbuild-sass-plugin'
import { ensureDirSync, writeFileSync } from "fs-extra";
import { rimraf } from "rimraf";
import { container } from 'tsyringe'
import { ROOT } from "./tokens";
import { watch } from 'chokidar'
import dayjs from "dayjs";
export const reactPlugin: Plugin = {
    name: 'react',
    setup: (build) => {
        build.onResolve({ filter: /^(react|antd)$/ }, args => {
            return {
                path: args.path,
                namespace: 'react-ns'
            }
        })
        build.onLoad({ filter: /^react$/, namespace: 'react-ns' }, () => {
            return {
                contents: 'module.exports = React'
            }
        })
        build.onLoad({ filter: /^antd$/, namespace: 'react-ns' }, () => {
            return {
                contents: 'module.exports = Antd'
            }
        })
    }
}
export async function watchAddon(scope: string, addon: string, isWatch: boolean = true) {
    const root = container.resolve(ROOT);
    const template = join(root, 'addons', `@${scope}`, addon, 'templates')
    const matcher = join(template, '**/*.tsx')
    const files = globSync(matcher.split(sep).join('/'))
    if (files.length <= 0) return;
    const entryFile = {}
    files.map(file => {
        const [, ...others] = file.replace(template, '').replace('.tsx', '').split(sep)
        const key = others.join('/')
        Reflect.set(entryFile, key, file)
    })
    const options: BuildOptions = {
        entryPoints: entryFile,
        outdir: join(root, `addons/@${scope}/${addon}/dist/public`),
        chunkNames: `chunks/[name]-[hash]`,
        splitting: true,
        bundle: true,
        minifyWhitespace: true,
        metafile: true,
        minify: isWatch ? false : true,
        sourcemap: false,
        treeShaking: true,
        target: ['chrome58'],
        format: 'esm',
        define: {},
        alias: {
            "@sker/sdk": join(root, 'themes/sdk/src/index.ts'),
            "@sker/model": join(root, 'themes/model/src/index.ts'),
            "@sker/ui": join(root, 'themes/ui/src/index.ts')
        },
        loader: {
            ".png": "file"
        },
        plugins: [
            reactPlugin,
            sassPlugin({
                filter: /\.module\.scss$/,
                transform: postcssModules({})
            }),
            sassPlugin({
                filter: /\.scss$/
            }),
        ]
    }
    let building: boolean = false;
    context(options).then(async b => {
        if (isWatch) {
            watch(matcher).on('all', async (eventName: string) => {
                if (building) return;
                building = true;
                console.log(`${dayjs().format('YYYY-MM-DD HH:mm:ss')}rebuild ${building} ${eventName}`)
                try {
                    const res = await b.rebuild()
                    building = false;
                    if (res.errors && res.errors.length > 0) {
                        console.log(res.errors)
                        return;
                    }
                    const outputs = res.metafile.outputs;
                    Object.keys(outputs).map(key => {
                        const output = outputs[key]
                        if (output.entryPoint) {
                            const imports = output.imports;
                            const htmlFile = key.replace('.js', '.html')
                            const html = createHtml(imports, key, `/@${scope}/${addon}/dist/${addon}.css`)
                            const filePath = join(root, htmlFile)
                            ensureDirSync(dirname(filePath))
                            writeFileSync(filePath, html)
                        }
                    });
                } catch (e) {
                    console.error((e as any).message)
                }
                return true;
            })
        }
        else {
            try {
                const res = await b.rebuild()
                building = false;
                if (res.errors && res.errors.length > 0) {
                    console.log(res.errors)
                    return;
                }
                const outputs = res.metafile.outputs;
                Object.keys(outputs).map(key => {
                    const output = outputs[key]
                    if (output.entryPoint) {
                        const imports = output.imports;
                        const htmlFile = key.replace('.js', '.html')
                        const html = createHtml(imports, key, `/@${scope}/${addon}/dist/public/${addon}.css`)
                        const filePath = join(root, htmlFile)
                        ensureDirSync(dirname(filePath))
                        writeFileSync(filePath, html)
                    }
                });
            } catch (e) {
                console.error((e as any).message)
            }
            await b.dispose();
            return true;
        }
    })
}

const createHtml = (chunks: {
    path: string
    kind: ImportKind | 'file-loader'
    external?: boolean
}[], page: string, css: string) => {
    return `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{title}}</title>
    <meta name="description" content="{{description}}">
    <meta name="keywords" content="{{keywords}}">
    <style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
    <link rel="stylesheet" href="/common.css">
    <link rel="stylesheet" href="/nger.css">
    <link rel="stylesheet" href="${css}">
    <script src="/react.js"></script>
    <script src="/icon.js"></script>
    <script>var SERVER_DATA = {{@ SERVER_DATA}}</script>
</head>
<body>
    <noscript>
      <strong>请启用JavaScript后再使用</strong>
    </noscript>
    <div id="app">
        <div class="first-loading-wrp">
            <div class="loading-wrp">
                <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
            </div>
            <div style="display: flex; justify-content: center; align-items: center;">Loading ...</div>
        </div>
    </div>
    ${chunks.map(chunk => {
        if (chunk.kind === 'import-statement') {
            const src = chunk.path.replace('addons', '')
            return `<script type="module" src="${src}"></script>`
        } else {
            console.log(chunk)
        }
    }).filter(it => !!it).join(`\n`)}
    <script type="module" src="${page.replace('addons', '')}"></script>
</body>
</html>`
}